"""
Performance Metrics and System Health Monitoring
================================================

Classes for tracking performance metrics and system health.
"""

import time
import logging
import psutil
import threading
from datetime import datetime
from typing import Optional, Dict, Any
from pathlib import Path


class PerformanceTracker:
    """
    Performance metrics tracking for recognition and network operations.
    """
    
    def __init__(self, logger_name: str = 'capswriter.performance'):
        self.logger = logging.getLogger(logger_name)
        self.start_times: Dict[str, float] = {}
    
    def start_operation(self, operation_id: str) -> None:
        """Start timing an operation."""
        self.start_times[operation_id] = time.time()
    
    def end_operation(self, operation_id: str, **extra_metrics) -> Optional[float]:
        """End timing an operation and log metrics."""
        if operation_id not in self.start_times:
            self.logger.warning(f"Operation {operation_id} was not started")
            return None
        
        duration = time.time() - self.start_times.pop(operation_id)
        
        self.logger.info(f"operation_completed", extra={
            'operation_id': operation_id,
            'duration_seconds': duration,
            'timestamp': datetime.utcnow().isoformat(),
            **extra_metrics
        })
        
        return duration
    
    def log_recognition_metrics(self, 
                              audio_duration: float,
                              processing_time: float, 
                              result_length: int,
                              model_name: str,
                              client_id: Optional[str] = None):
        """Log speech recognition performance metrics."""
        
        real_time_factor = processing_time / audio_duration if audio_duration > 0 else 0
        
        self.logger.info("recognition_completed", extra={
            'audio_duration_seconds': audio_duration,
            'processing_time_seconds': processing_time,
            'real_time_factor': real_time_factor,
            'result_character_count': result_length,
            'characters_per_second': result_length / processing_time if processing_time > 0 else 0,
            'model_name': model_name,
            'client_id': client_id,
            'timestamp': datetime.utcnow().isoformat()
        })
    
    def log_websocket_metrics(self, 
                            client_id: str,
                            message_type: str, 
                            message_size: int,
                            processing_time: float,
                            direction: str = 'inbound'):
        """Log WebSocket message processing metrics."""
        
        self.logger.info("websocket_message", extra={
            'client_id': client_id,
            'message_type': message_type,
            'message_size_bytes': message_size,
            'processing_time_ms': processing_time * 1000,
            'direction': direction,
            'throughput_bytes_per_sec': message_size / processing_time if processing_time > 0 else 0,
            'timestamp': datetime.utcnow().isoformat()
        })
    
    def log_audio_processing_metrics(self,
                                   segment_duration: float,
                                   processing_time: float,
                                   audio_format: str,
                                   sample_rate: int,
                                   channels: int):
        """Log audio processing performance metrics."""
        
        self.logger.info("audio_processing", extra={
            'segment_duration_seconds': segment_duration,
            'processing_time_seconds': processing_time,
            'real_time_factor': processing_time / segment_duration if segment_duration > 0 else 0,
            'audio_format': audio_format,
            'sample_rate': sample_rate,
            'channels': channels,
            'timestamp': datetime.utcnow().isoformat()
        })
    
    def log_file_operation_metrics(self,
                                 operation: str,
                                 file_path: str,
                                 file_size: int,
                                 processing_time: float):
        """Log file operation metrics."""
        
        throughput = file_size / processing_time if processing_time > 0 else 0
        
        self.logger.info("file_operation", extra={
            'operation': operation,
            'file_path': file_path,
            'file_size_bytes': file_size,
            'processing_time_seconds': processing_time,
            'throughput_bytes_per_sec': throughput,
            'timestamp': datetime.utcnow().isoformat()
        })


class SystemHealthLogger:
    """
    System resource monitoring and health tracking.
    """
    
    def __init__(self, logger_name: str = 'capswriter.system.health'):
        self.logger = logging.getLogger(logger_name)
        self.start_time = time.time()
        self.process = psutil.Process()
        self._monitoring = False
        self._monitor_thread: Optional[threading.Thread] = None
    
    def log_system_stats(self) -> Dict[str, Any]:
        """Log current system health statistics."""
        
        try:
            # Memory information
            memory_info = self.process.memory_info()
            memory_percent = self.process.memory_percent()
            
            # CPU information
            cpu_percent = self.process.cpu_percent()
            
            # System information
            system_memory = psutil.virtual_memory()
            system_cpu = psutil.cpu_percent(interval=1)
            
            # Disk usage for logs directory
            logs_path = Path('logs')
            disk_usage = psutil.disk_usage(str(logs_path.parent) if logs_path.exists() else '.')
            
            stats = {
                'process_memory_mb': memory_info.rss / 1024 / 1024,
                'process_memory_percent': memory_percent,
                'process_cpu_percent': cpu_percent,
                'system_memory_percent': system_memory.percent,
                'system_cpu_percent': system_cpu,
                'disk_usage_percent': (disk_usage.used / disk_usage.total) * 100,
                'uptime_seconds': time.time() - self.start_time,
                'thread_count': self.process.num_threads(),
                'open_files_count': len(self.process.open_files()) if hasattr(self.process, 'open_files') else 0,
                'timestamp': datetime.utcnow().isoformat()
            }
            
            self.logger.info("system_health", extra=stats)
            return stats
            
        except Exception as e:
            self.logger.error(f"Failed to collect system stats: {e}")
            return {}
    
    def start_monitoring(self, interval: int = 60) -> None:
        """Start periodic system health monitoring."""
        
        if self._monitoring:
            self.logger.warning("System monitoring already running")
            return
        
        self._monitoring = True
        
        def monitor_loop():
            while self._monitoring:
                try:
                    self.log_system_stats()
                    time.sleep(interval)
                except Exception as e:
                    self.logger.error(f"Error in monitoring loop: {e}")
                    time.sleep(interval)
        
        self._monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
        self._monitor_thread.start()
        
        self.logger.info(f"Started system health monitoring with {interval}s interval")
    
    def stop_monitoring(self) -> None:
        """Stop periodic system health monitoring."""
        
        if not self._monitoring:
            return
        
        self._monitoring = False
        if self._monitor_thread:
            self._monitor_thread.join(timeout=5)
        
        self.logger.info("Stopped system health monitoring")
    
    def log_startup_info(self) -> None:
        """Log system information at startup."""
        
        try:
            import platform
            
            startup_info = {
                'platform': platform.platform(),
                'python_version': platform.python_version(),
                'cpu_count': psutil.cpu_count(),
                'total_memory_gb': psutil.virtual_memory().total / (1024**3),
                'disk_total_gb': psutil.disk_usage('.').total / (1024**3),
                'process_id': self.process.pid,
                'start_time': datetime.utcnow().isoformat()
            }
            
            self.logger.info("system_startup", extra=startup_info)
            
        except Exception as e:
            self.logger.error(f"Failed to log startup info: {e}")
    
    def check_resource_thresholds(self, 
                                memory_threshold: float = 80.0,
                                cpu_threshold: float = 80.0,
                                disk_threshold: float = 90.0) -> Dict[str, bool]:
        """Check if system resources exceed thresholds."""
        
        try:
            stats = self.log_system_stats()
            
            alerts = {
                'memory_alert': stats.get('process_memory_percent', 0) > memory_threshold,
                'cpu_alert': stats.get('process_cpu_percent', 0) > cpu_threshold, 
                'disk_alert': stats.get('disk_usage_percent', 0) > disk_threshold
            }
            
            # Log warnings if thresholds exceeded
            if alerts['memory_alert']:
                self.logger.warning(f"Memory usage exceeded threshold: {stats.get('process_memory_percent', 0):.1f}%")
            
            if alerts['cpu_alert']:
                self.logger.warning(f"CPU usage exceeded threshold: {stats.get('process_cpu_percent', 0):.1f}%")
            
            if alerts['disk_alert']:
                self.logger.warning(f"Disk usage exceeded threshold: {stats.get('disk_usage_percent', 0):.1f}%")
            
            return alerts
            
        except Exception as e:
            self.logger.error(f"Failed to check resource thresholds: {e}")
            return {'memory_alert': False, 'cpu_alert': False, 'disk_alert': False}


class ConnectionTracker:
    """
    Track WebSocket connections and their lifecycle.
    """
    
    def __init__(self, logger_name: str = 'capswriter.server.connections'):
        self.logger = logging.getLogger(logger_name)
        self.active_connections: Dict[str, Dict[str, Any]] = {}
    
    def connection_established(self, client_id: str, client_info: Dict[str, Any]) -> None:
        """Log new connection establishment."""
        
        connection_data = {
            'established_at': datetime.utcnow().isoformat(),
            'client_info': client_info,
            'messages_sent': 0,
            'messages_received': 0,
            'bytes_sent': 0,
            'bytes_received': 0
        }
        
        self.active_connections[client_id] = connection_data
        
        self.logger.info("connection_established", extra={
            'client_id': client_id,
            'client_ip': client_info.get('remote_address', 'unknown'),
            'user_agent': client_info.get('user_agent', 'unknown'),
            'total_connections': len(self.active_connections),
            'timestamp': connection_data['established_at']
        })
    
    def connection_closed(self, client_id: str, reason: str = 'unknown') -> None:
        """Log connection closure."""
        
        if client_id in self.active_connections:
            connection_data = self.active_connections.pop(client_id)
            
            established_at = datetime.fromisoformat(connection_data['established_at'])
            duration = (datetime.utcnow() - established_at).total_seconds()
            
            self.logger.info("connection_closed", extra={
                'client_id': client_id,
                'reason': reason,
                'duration_seconds': duration,
                'messages_sent': connection_data['messages_sent'],
                'messages_received': connection_data['messages_received'],
                'bytes_sent': connection_data['bytes_sent'],
                'bytes_received': connection_data['bytes_received'],
                'remaining_connections': len(self.active_connections),
                'timestamp': datetime.utcnow().isoformat()
            })
    
    def log_message_stats(self, client_id: str, direction: str, size: int) -> None:
        """Update message statistics for a connection."""
        
        if client_id in self.active_connections:
            connection = self.active_connections[client_id]
            
            if direction == 'sent':
                connection['messages_sent'] += 1
                connection['bytes_sent'] += size
            elif direction == 'received':
                connection['messages_received'] += 1
                connection['bytes_received'] += size
    
    def get_connection_summary(self) -> Dict[str, Any]:
        """Get summary of all active connections."""
        
        total_messages = sum(conn['messages_sent'] + conn['messages_received'] 
                           for conn in self.active_connections.values())
        
        total_bytes = sum(conn['bytes_sent'] + conn['bytes_received']
                         for conn in self.active_connections.values())
        
        summary = {
            'active_connections': len(self.active_connections),
            'total_messages': total_messages,
            'total_bytes': total_bytes,
            'timestamp': datetime.utcnow().isoformat()
        }
        
        self.logger.info("connection_summary", extra=summary)
        return summary